//
// Created by heqian on 17-8-4.
//

#include "Identify.h"


namespace mahjong {


    Identify::Identify() {
        mTemplate = new Template();
        mEdit = EditImage::get();
        mMinMatching = 0.85;
        mBoardInfo = new BoardInfo();
    }

    Identify::~Identify() {
    }

    MahjongEnum Identify::discernImage(Prt<Image> &img, vector<Prt<Item>> &items) {
        ImageChannel channel = (ImageChannel) -1;
        int binary = -1;
        Prt<Image> hold;
        MahjongEnum ret = M_NONE;
        long tValue, value = -1;
        Prt<EditImage> &edit = mEdit;
        for (Prt<Item> &item:items) {
            Prt<Image> templ = item->getTempl();

            if (item->getChannel() != channel || item->getBinary() != binary) {
                hold = (item->getChannel() == CHANNEL_NONE)
                       ? edit->gray(img) : edit->channel(img, item->getChannel());

                hold = edit->threshold(hold, item->getBinary());
                channel = item->getChannel();
                binary = item->getBinary();
            }

            tValue = MatchImage::get()->overlapArea(hold, templ);
            if (tValue > value) {
                ret = item->getValue();
                value = tValue;
            }
        }
        if (value / (double) (img->getWidth() * img->getHeight()) < mMinMatching)
            return M_NONE;
        return ret;
    }

    MahjongEnum Identify::lackMahjong(Rect rect) {
        vector<Prt<Item>> items;
        items.push_back(mTemplate->getMTong());
        items.push_back(mTemplate->getMTiao());
        items.push_back(mTemplate->getMWan());

        Prt<Image> img = mEdit->clip(mImage, rect);
        return discernImage(img, items);
    }

    void Identify::start(Prt<Image> &image) {
        mImage = image;

        getUpBoard();
        getDownBoard();
        getTopBoard();
        getMeBoard();
    }

    bool Identify::isDealer(Rect rect) {
        Prt<Image> img = mEdit->clip(mImage, rect);
        Prt<Item> zhuang = mTemplate->getMZhuang();
        return M_Zhuang == discernValue(img, zhuang);
    }

    MahjongEnum Identify::discernValue(Prt<Image> &img, Prt<Item> &item) {
        Prt<EditImage> &edit = mEdit;
        Prt<Image> hold = (item->getChannel() == CHANNEL_NONE)
                          ? edit->gray(img) : edit->channel(img, item->getChannel());
        hold = edit->threshold(hold, item->getBinary());
        Prt<Image> templ = item->getTempl();
        double value = MatchImage::get()->overlapArea(hold, templ);
        if (value / (img->getWidth() * img->getHeight()) > mMinMatching)
            return item->getValue();
        return M_NONE;
    }

    void
    Identify::discernBoards(Prt<vector<MahjongEnum>> board, Rect &rect, vector<Prt<Item>> &items,
                            int width, int height, Rect &templRect) {
        int conutX = rect.getWidth() / width;
        int conutY = rect.getHeight() / height;

        Prt<Image> img;
        MahjongEnum val;
        int dy, dx;
        board->clear();
        for (int y = 0; y < conutY; ++y) {
            dy = rect.getY() + y * height + templRect.getY();
            for (int x = 0; x < conutX; ++x) {
                dx = rect.getX() + x * width + templRect.getX();
                img = mEdit->clip(mImage, dx, dy, templRect.getWidth(),
                                  templRect.getWidth());

                if (M_NONE != (val = discernImage(img, items)))
                    board->push_back(val);
            }
        }
    }


    double Identify::getMinMatching() const {
        return mMinMatching;
    }

    void Identify::setMinMatching(double minMatching) {
        Identify::mMinMatching = minMatching;
    }

    int Identify::scanBottom(Prt<Image> img, Rect &rect, int x) const {
        DrawRect dRect(img->getWidth(), img->getHeight(), rect);
        int dy = dRect.getDHeight();
        int width = img->getWidth();
        uint8_t *imgData = (uint8_t *) img->getData() - width + x;
        for (int y = dRect.getDHeight() - 1; y >= dy; --y, imgData -= width) {
            if (*(imgData + x))
                return x;
        }
        return 0;
    }

    int Identify::scanTop(Prt<Image> img, Rect &rect, int x) const {
        DrawRect dRect(img->getWidth(), img->getHeight(), rect);
        int height = dRect.getDHeight();
        int width = img->getWidth();
        uint8_t *imgData = (uint8_t *) img->getData() + x;
        for (int y = dRect.getSY(); y < height; ++y, imgData += width) {
            if (*(imgData + x))
                return x;
        }
        return 0;
    }

    int Identify::scanRight(Prt<Image> img, Rect &rect, int y) const {
        DrawRect dRect(img->getWidth(), img->getHeight(), rect);
        int dx = dRect.getSX();
        uint8_t *imgData = (uint8_t *) img->getData() + (y + dRect.getSY()) * img->getWidth();
        for (int x = dRect.getDWidth() - 1; x >= dx; --x) {
            if (*(imgData + x))
                return x;
        }
        return 0;
    }

    int Identify::scanLeft(Prt<Image> img, Rect &rect, int y) const {
        DrawRect dRect(img->getWidth(), img->getHeight(), rect);
        int width = dRect.getSX() + dRect.getDWidth();
        uint8_t *imgData = (uint8_t *) img->getData() + (y + dRect.getSY()) * img->getWidth();
        for (int x = dRect.getSX(); x < width; ++x) {
            if (*(imgData + x))
                return x;
        }
        return 0;
    }

    void Identify::getUpBoard() {
        Prt<UserBoard> user = mBoardInfo->getUp();
        Size size(45, 24);
        Prt<vector<MahjongEnum>> board = user->getOpen();
        getOpenBoard(board, mTemplate->getUpBoard(), mTemplate->getScanUp(), size);

        board = user->getOut();
        getBoard(board, mTemplate->getUpBoard(), mTemplate->getScanUpOut(), size);

        user->setLackBoard(lackMahjong(mTemplate->getUpLack()));
        user->setIsDealer(isDealer(mTemplate->getUpDealer()));
    }

    void Identify::getDownBoard() {
        Prt<UserBoard> user = mBoardInfo->getDown();
        Size size(45, 24);
        Prt<vector<MahjongEnum>> board = user->getOpen();
        getOpenBoard(board, mTemplate->getDownBoard(), mTemplate->getScanDown(), size);

        board = user->getOut();
        getBoard(board, mTemplate->getDownBoard(), mTemplate->getScanDownOut(), size);

        user->setLackBoard(lackMahjong(mTemplate->getDownLack()));
        user->setIsDealer(isDealer(mTemplate->getDownDealer()));
    }

    void Identify::getTopBoard() {
        Prt<UserBoard> user = mBoardInfo->getTop();
        Size size(33, 39);
        Prt<vector<MahjongEnum>> board = user->getOpen();
        getOpenBoard(board, mTemplate->getTopBoard(), mTemplate->getScanTop(), size);

        board = user->getOut();
        getBoard(board, mTemplate->getTopBoard(), mTemplate->getScanTopOut(), size);

        user->setLackBoard(lackMahjong(mTemplate->getTopLack()));
        user->setIsDealer(isDealer(mTemplate->getTopDealer()));
    }

    void Identify::getMeBoard() {
        Prt<UserBoard> user = mBoardInfo->getMe();

        Size size(48, 58);
        Prt<vector<MahjongEnum>> board = user->getOpen();
        getOpenBoard(board, mTemplate->getMeOpenBoard(), mTemplate->getMScanMeOpen(), size);

        board = user->getOut();
        size.setWidth(33);
        size.setHeight(39);
        getBoard(board, mTemplate->getMeOutBoard(), mTemplate->getScanMeOut(), size);

        user->setLackBoard(lackMahjong(mTemplate->getMeLack()));
        user->setIsDealer(isDealer(mTemplate->getMeDealer()));

        Prt<Image> red = mEdit->channel(mImage, CHANNEL_RED);
        red = mEdit->threshold(red, 90);
        Rect rect(47, 580, 1006, 118);
        int x = scanLeft(red, rect, 8);

        Rect brect(x, rect.getY(), rect.getWidth() - (x - rect.getX()), rect.getHeight());
        Rect tbrect(6, 29, 71, 83);
        board = user->getBoard();

        discernBoards(board, brect, mTemplate->getMeBoard(), 81, 118, tbrect);
    }

    void
    Identify::getOpenBoard(Prt<vector<MahjongEnum>> board, vector<Prt<Item>> &templ,
                           vector<ScanPos> &poss, Size &size) {
        Prt<Image> img;
        MahjongEnum val;
        board->clear();

        for (ScanPos &pos:poss) {
            img = mEdit->clip(mImage, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());

            if (M_NONE != (val = discernImage(img, templ))) {
                board->push_back(val);
                board->push_back(val);
                board->push_back(val);
            } else if (0 != pos.getOffsetX() || 0 != pos.getOffsetY()) {
                img = mEdit->clip(mImage, pos.getX() + pos.getOffsetX(),
                                  pos.getY() + pos.getOffsetY(), size.getWidth(), size.getHeight());

                if (M_NONE != (val = discernImage(img, templ))) {
                    board->push_back(val);
                    board->push_back(val);
                    board->push_back(val);
                    board->push_back(val);
                } else {
                    return;
                }
            }
        }
    }

    void Identify::getBoard(Prt<vector<MahjongEnum>> board, vector<Prt<Item>> &templ,
                            vector<ScanPos> &poss, Size &size) {
        Prt<Image> img;
        MahjongEnum val;
        board->clear();

        for (ScanPos &pos:poss) {
            img = mEdit->clip(mImage, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());

            if (M_NONE != (val = discernImage(img, templ))) {
                board->push_back(val);
            } else {
                return;
            }
        }
    }

    Prt<BoardInfo> &Identify::getMBoardInfo() {
        return mBoardInfo;
    }


}